bzoj 4816 [Sdoi2017]数字表格.md

题目链接

bzoj4816: [Sdoi2017]数字表格

题解

满满的反演的套路的味道

常规操作枚举约数

上面的式子可以化为

对于右上角的式子

很眼熟…推下式子

令$p=id$那么原式变成了

对于每个$p$,令$F[p]=\prod_{d|p} f[d]^{\mu(\frac{p}{d})}$,$F[p]$的值是固定的,用筛法求出$F[p]$,做前缀积,对与$\left \lfloor \frac{n}{p} \right \rfloor \left \lfloor \frac{m}{p} \right \rfloor$数论分块一下
复杂度为$O((n+T\sqrt{n})log(1e9+7))$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<cstdio>
#include<algorithm>
const int maxn = 1000007;
const int mod = 1e9+7;
int f[maxn+7],invf[maxn+7],F[maxn+7];
bool isprime[maxn+7];int mu[maxn+7],prime[maxn+7],num;
int pow(int a,int p) {
int ret=1;
for(;p;p>>=1,a=(1LL*a*a)%mod){
if(p&1) ret=(1LL*ret*a)%mod;
}
return ret;
}
void init() {
f[1]=1;
for(int i=2;i<maxn;++i) f[i]=(f[i-1]+f[i-2])%mod;
for(int i=1;i<maxn;++i) {
invf[i]=pow(f[i],mod-2)%mod;
//printf("%d %d\n",invf[i],f[i]);
}
isprime[1]=1;mu[1]=1;F[0]=F[1]=1;
//printf("%d\n",mu[1]);
for(int i=2;i<=maxn-7;++i) {
F[i]=1;
if(!isprime[i]) prime[++num]=i,mu[i]=-1;
for(int j=1;j<=num&&prime[j]*i<=maxn-7;++j) {
isprime[i*prime[j]]=1;
if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=maxn-7;++i) {
if(mu[i]==0)continue;
for(int j=i;j<=maxn-7;j+=i) {
if(mu[i]==1) F[j]=(1ll*F[j]*f[j/i])%mod;
if(mu[i]==-1) F[j]=(1ll*F[j]*invf[j/i])%mod;
}
}
for(int i=1;i<=maxn-7;++i) F[i]=(1ll*F[i]*F[i-1])%mod;//,printf("%d\n",F[i]);
}
int query(int a,int b) {
int n=std::min(a,b);long long ans=1;
for(int nxt,i=1;i<=n;i=nxt+1) {
nxt=std::min(a/(a/i),b/(b/i));
long long tmp=1ll*F[nxt]*pow(F[i-1],mod-2)%mod;
ans=(1ll*ans*pow(tmp,1ll*(a/i)*(b/i)%(mod-1)))%mod;
}
return (ans+mod)%mod;
}
int main() {
//freopen("001.out","w",stdout);
init();
int T;scanf("%d",&T);
for(int a,b;T--;) {
scanf("%d%d",&a,&b);
printf("%d\n",query(a,b));
}
return 0;
}